package com.md.util;

import com.md.base.exception.ServerException;
import com.md.model.bo.CustomThreadFactory;
import com.md.value.constants.RetryConstants;
import com.md.value.enums.ErrorEnum;
import com.md.value.server.ServerContext;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author 刘骄阳
 * 2023-08-09 18:38
 */
@Slf4j
@UtilityClass
public class ThreadUtil {
  /**
   * 运行 Job 任务
   *
   * @param runnable 任务函数
   * @param jobName  任务名称
   */
  public static void run(final Runnable runnable, final String jobName) {
    log.info("Start execute function, jobName: {}", jobName);
    try {
      runnable.run();
    } catch (Exception e) {
      log.error("Run function failed, cause by: {}", e.getMessage(), e);
    }
    log.info("Completed execute function, jobName: {}", jobName);
  }

  /**
   * 异步运行 Job 任务
   *
   * @param runnable 任务函数
   */
  public static void runAsync(final Runnable runnable, final String jobName) {
    CompletableFuture.runAsync(() -> {
      log.info("Start execute function, jobName: {}", jobName);
      try {
        runnable.run();
      } catch (Exception e) {
        log.error("Run function failed, cause by: {}", e.getMessage(), e);
      } finally {
        ServerContext.clear();
      }
      log.info("Completed execute function, jobName: {}", jobName);
    });
  }

  /**
   * 异步运行 Job 任务
   *
   * @param runnable 任务函数
   * @param executor 指定线程池
   */
  public static void runAsync(final Runnable runnable, final String jobName, final Executor executor) {
    CompletableFuture.runAsync(() -> {
      log.info("Start execute function, jobName: {}", jobName);
      try {
        runnable.run();
      } catch (Exception e) {
        log.error("Run function failed, cause by: {}", e.getMessage(), e);
      } finally {
        ServerContext.clear();
      }
      log.info("Completed execute function, jobName: {}", jobName);
    }, executor);
  }

  public static Executor newInstanceExecutor(final Integer coreThreadCount, final String namePrefix) {
    return Executors.newFixedThreadPool(coreThreadCount, CustomThreadFactory.of(namePrefix));
  }

  public static Executor newInstanceExecutor(final Integer coreThreadCount) {
    return Executors.newFixedThreadPool(coreThreadCount, CustomThreadFactory.of("thread"));
  }

  @SafeVarargs
  public static <T> T executeWithRetry(final Callable<T> task, final long maxAttempts, final long backoffInMillis,
                                       final Class<? extends Exception>... retryAbleExceptions) {
    int attempt = 0;
    while (attempt == 0 || attempt < maxAttempts) {
      try {
        return task.call();
      } catch (Exception ex) {
        if (MatchUtil.isRetryableException(ex, retryAbleExceptions)) {
          attempt++;
          try {
            Thread.sleep(backoffInMillis);
          } catch (InterruptedException e) {
            log.error("重试: 睡眠异常: {}", e.getMessage(), e);
          }
        } else {
          log.error("重试失败上限: {}", ex.getMessage(), ex);
          throw ServerException.of(ErrorEnum.SYSTEM_ERROR);
        }
      }
    }
    throw ServerException.of(ErrorEnum.SYSTEM_ERROR);
  }

  public static <T> T executeWithRetry(final Callable<T> task,
                                       final Class<? extends Exception>... retryableExceptions) {
    return executeWithRetry(task, RetryConstants.MAX_RETRY_COUNT, RetryConstants.SLEEP_MILLIONS_SECONDS,
      retryableExceptions);
  }
}
